<?php
/* !
** JSAM - JavaScript-Manager for admin
** @version 0.0.2beta1
** @description: JavaScript-инструментарий менеджера MODx для настройки внешнего вида административной панели
** @author: Александр Кузнецов aka Regent(http://vl.vg/)
** @temp_home_page: http://vj.vg/
** @this_type: General PHP code
** Dual licensed under the MIT and GPL licenses.
** - http://www.opensource.org/licenses/mit-license.php
** - http://www.gnu.org/copyleft/gpl.html
*/
class JSAM{
	
	/*****************************\
	|***** Переменные класса *****|
	\*****************************/
	
	// Текущая версия JSAM
	var $version = '0.0.2beta1';
	
	// Тип фрейма. Высчитывается при инициализации класса
	var $type = '';
	
	// Номер REQUEST-действия
	var $action = '';
	
	// Явное имя подключаемого фрейма
	var $frame = '';
	
	// Тип документа
	var $DOCTYPE = '';
	
	// Тип документа по умолчанию
	var $defaultDOCTYPE = '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">';
	
	// Массив стилей фрейма
	var $style = array();
	// Массив адресов к каскадным таблицам стилей
	var $styleUrl = array();
	
	// Код, который печатается во фрейме с помощью jsam
	var $code = "";
	
	// Карта документов - Ассоциативный массив JavaScript
	var $map = '{"length":0,"max":0}';
	// Карта документов - PHP массив
	var $documentsMap = array();
	
	// Язык административной панели
	var $lang = 'en';
	
	// Признаки принадлежности фреймов
	var $isDocument = false;
	var $isWorkFrame = false;
	
	// ID ресурса
	var $id = 0;
	// ID родителя ресурса
	var $parent = 0;
	
	// Информация о родителе ресурса
	var $parentInfo = array();
	
	// Дефолтные опции
	var $options = array(
		
		// Управление оверлеем
		// Если значение равно false, то оверлей не создаётся
		'overlay' => array(
			
			// Опции оверлея по умолчанию
			'default' => array(
				'style' => 'background-color:#C4DCFB;',
				'opacity' => 0.9,
				'show' => true
			),
			
			// Опции оверлея для меню
			'menu' => array(
				'style' => 'background-color:#555;',
				'opacity' => 0.82
			),
			
			// Опции оверлея для дерева документов
			'tree' => array(
				'style' => 'background-color:#FDEEB5;'
			),
			
			'home' => array(
				'style' => 'background-color:#F7F7F7;'
			)
			
		),
		
		// Дополнительные вставляемые стили
		'style' => '',
		
		// Вариант подключения библиотеки jQuery
		//   1 - подключение с Google Code
		//   0 - подключение из папки плагина
		'jquery' => 1,
		
		// Ссылка на jQuery с Google Code
		'jqueryUrl' => 'http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js'
		
	);
	
	/***********************\
	|***** Конструктор *****|
	\***********************/
	function JSAM(){
		
		global $modx;
		
		// Быстрая ссылка на объект event
		$this -> e = &$modx -> event;		
		
		// Номер REQUEST-действия
		$this -> action = array_key_exists( 'action', $this -> e -> params ) ? $this -> e -> params[ 'action' ] : '';
		
		// Явное название подключаемого фрейма
		$this -> frame = isset( $_REQUEST[ 'f' ] ) ? preg_replace( '/[^a-z0-9]/i', '', $_REQUEST[ 'f' ] ) : '';
		
		// Определение типа фрейма
		switch( $this -> action ){
			// REQUEST действие №1 - подключение PHP файла из папки manager/frames/
			// Возможные типы: "Меню" или "Дерево ресурсов"
			case 1:
				if( $this -> frame == 'tree' || $this -> frame == 'menu' ){
					$this -> type = $this -> frame;
				}
				break;
			// Главная страница
			case 2:
				$this -> type = 'home';
				break;
			// Создание/Редактирование ресурса
			case 4:
			case 27:
				$this -> type = 'document';
				$this -> isDocument = true;
				break;
			// Редактирование чанка
			case 78:
				$this -> type = 'chunk';
				break;
			default:
				break;
		}
		
		// Определение языка системы
		switch( $modx -> config[ 'manager_language' ] ){
			case 'russian':
			case 'russian-UTF8':
				$this -> lang = 'ru';
				break;
			default:
				break;
		}
		
		$this -> isWorkFrame = $this -> type != 'menu' && $this -> type != 'tree';
		
		// Создание url`ов
		$this -> siteUrl = $modx -> config[ 'site_url' ];
		$this -> assetsUrl = $this -> siteUrl . preg_replace( '/^\/|\/$/s', '', $modx -> config[ 'rb_base_url' ] ) . '/';
		
		// Получение ID ресурса из массива $_REQUEST
		if( isset( $_REQUEST[ 'id' ] ) ){
			$this -> id = (int)$_GET[ 'id' ];
		}
		// Получение ID родителя ресурса из массива $_REQUEST
		if( isset( $_REQUEST[ 'pid' ] ) ){
			$this -> parent = (int)$_GET[ 'pid' ];
		}
		
		// Построение карты документов
		if( $this -> isDocument ){
			$map = $modx -> db -> query ( "SELECT `id`, `parent` FROM " . $modx -> db -> config[ 'table_prefix' ] . "site_content ORDER BY `id`" );
			$this -> documentsMap = $modx -> db -> makeArray( $map );
			$max = end( $this -> documentsMap );
			$map = '';
			foreach( $this -> documentsMap as $doc ){
				if( $doc[ 'id' ] == $this -> id ){
					$this -> parent = $doc[ 'parent' ];
				}
				$map .= $doc[ 'id' ] . ':' . $doc[ 'parent' ] . ',';
			}
			$this -> map = '{' . $map . '"length":' . count( $this -> documentsMap ) . ',"max":' . $max[ 'id' ] . '}';
			if( $this -> parent != 0 ){
				$parentInfo = $modx -> db -> query ( "SELECT * FROM " . $modx -> db -> config[ 'table_prefix' ] . "site_content WHERE `id` = " . $this -> parent );
				$this -> parentInfo = $modx -> db -> makeArray( $parentInfo );
				$this -> parentInfo = $this -> parentInfo[ 0 ];
			}
		}
		
		// Данные фрейма
		$this -> data = array(
			'config' => array(
				'url'                 => $this -> siteUrl,
				'assets'              => $this -> assetsUrl,
				'path'                => $this -> assetsUrl . 'plugins/jsam/',
				'language'            => $modx -> config[ 'manager_language' ],
				'role'                => $_SESSION[ 'mgrRole' ],
				'shortname'           => $_SESSION[ 'mgrShortname' ],
				'email'               => $_SESSION[ 'mgrEmail' ],
				'modx'                => $modx -> config[ 'settings_version' ],
				'jsam'                => $this -> version,
				'default_template'    => $modx -> config[ 'default_template' ]
			),
			'id'      => $this -> id,
			'parent'  => $this -> parent,
			'type'    => $this -> type
		);		
		
	}
	
	/*******************************\
	|***** Управляющие функции *****|
	\*******************************/
	
	// Функция добавления кода к конечному выводу
	function output( $code ){
		return $this -> code .= $code;
	}
	
	// Функция вывода конечного кода
	function printCode(){
		echo strlen( $this -> code ) ? ( $this -> DOCTYPE == '' ? $this -> defaultDOCTYPE : $this -> DOCTYPE ) . "\n" . $this -> code : '';
	}
	
	// Рекурсивное расширение одного массива другим
	// (c) http://www.php.net/manual/en/function.array-merge.php
	function extend( $a, $b ){
		foreach( $b as $k => $v ){
			if( is_array( $v ) ){
				if( !isset( $a[ $k ] ) ){
					$a[ $k ] = $v;
				}
				else{
					$a[ $k ] = $this -> extend( $a[ $k ], $v );
				}
			}
			else{
				$a[ $k ] = $v;
			}
		}
		return $a;
	}
	
	// Расширение опций новыми опциями
	function extendOptions( $opt ){
		if( is_array( $opt ) ){
			$this -> options = $this -> extend( $this -> options, $opt );
		}
	}
	
	// Основное подключение JSAM в документ
	function start(){
		
		global $modx;
		// Быстрые ссылки
		$type    = $this -> type;
		$opt     = $this -> options;
		$config  = $this -> data[ 'config' ];
		$path    = $config[ 'path' ];
		
		// Если тип фрейма поддерживается, то происходит подключение
		if( $type != '' ){
			
			$modx -> invokeEvent( 'OnJsamBeforeExecution' );
			
			// Обозначения начала кода от JSAM
			$this -> output( "<!-- © JSAM Code Start -->\n" );
			
			// Подключение основного стилевого файла
			$this -> styleUrl[] = $path . 'assets/css/style.css';
			
			// Постройка оверлея
			if( $opt[ 'overlay' ] ){
				$overlay = $opt[ 'overlay' ][ 'default' ];
				if( array_key_exists( $type, $opt[ 'overlay' ] ) ){
					$overlay = is_array( $opt[ 'overlay' ][ $type ] ) ? $this -> extend( $overlay, $opt[ 'overlay' ][ $type ] ) : false;
				}
				if( $overlay ){
					$overlayStyle = '';
					if( $overlay[ 'opacity' ] ){
						$overlayStyle .= 'opacity:' . $overlay[ 'opacity' ] . ';';
					}
					if( $overlay[ 'style' ] ){
						$overlayStyle .= $overlay[ 'style' ];
					}
					if( $overlayStyle ){
						$this -> style[] = '#jsamOverlay{' . $overlayStyle . '}';
					}
					$this -> output( "<div id='jsamOverlay'" . ( $this -> isWorkFrame ? ' class="WorkFrame"' : '' ) . "></div>\n" );
					$this -> style[] = '#preLoader{display:none;}';
				}
			}
			
			// Основной код
			$this -> output(
				"<script type='text/javascript' src='" . ( $opt[ 'jquery' ] != 0 ? $opt[ 'jqueryUrl' ] : $path . 'assets/js/jquery.min.js' ) . "'></script>\n"
				. "<script type='text/javascript'>\nwindow.jsam = {\n"
				. "\t'data'        : " . json_encode( $this -> data ) . ",\n"
				. "\t'lang'        : '" . $this -> lang . "',\n"
				. "\t'jQuery'      : jQuery.noConflict( true ),\n"
				. "\t'map'         : " . $this -> map . ",\n"
				. "\t'parentInfo'  : " . json_encode( $this -> parentInfo ) . "\n"
				. "\n}\n"
				. "</script>\n"
				. "<script type='text/javascript' src='" . $path . "jsam.js?" . rand() . rand() . "'></script>\n"
				. "<script type='text/javascript' src='" . $path . 'jsam_rules.js?' . rand() . "'></script>\n"
			);
			
			// Обозначения конца кода от JSAM
			$this -> output( "<!-- JSAM Code End -->\n" );
			
			$modx -> invokeEvent( 'OnJsamAftereExecution' );
			
		}
	}
	
	// Добавить на событие $eventName выполнение кода $code
	function event( $eventName, $code ){
		global $modx;
		$pluginName = 'JSAM-' . $this -> eventCount;
		$modx -> pluginCache[ $pluginName ] = $code;
		if( !array_key_exists( $eventName, $modx -> pluginEvent ) ){
			$modx -> pluginEvent[ $eventName ] = array();
		}
		$modx -> pluginEvent[ $eventName ][] = $pluginName;
		$this -> eventCount++;
		return $pluginName;
	}
	var $eventCount = 0;
	
}

// JSAM должен инициализироваться только по наступлению событя OnManagerPageInit
if( $modx -> event -> name == 'OnManagerPageInit' ){
	
	// Инициализация
	$jsam = new JSAM();
	
	// Добавление ссылки на $jsam к объекту $modx
	$modx -> jsam = &$jsam;	
	
	// Расширение дефолтных опций пользовательскими опциями
	if( isset( $jsamOptions ) ){
		$jsam -> extendOptions( $jsamOptions );
	}
	
	$modx -> invokeEvent( 'OnJsamCreated' );
	
	$jsam -> start();
	
	$modx -> invokeEvent( 'OnJsamBeforeLoadExtension' );
	
	// Расширения
	$dir = $modx -> config[ 'base_path' ] . 'assets/plugins/jsam/extensions';
	if( $handle = opendir( $dir ) ){
		$ignore_first_chars = array( '.', '_', '!' ); 
		while( false !== ( $file = readdir( $handle ) ) ){
			if( !in_array( substr( $file, 0, 1 ), $ignore_first_chars ) && $file != ".." && substr( $file, -8 ) == '.inc.php' ){
				$url = $dir . '/' . $file;
				$modx -> invokeEvent( 'OnJsamExtension', array( $url ) );
				include( $url );
			}
		}
		closedir( $handle );
	}
	
	$modx -> invokeEvent( 'OnJsamLoadExtension' );
	
	// Пропечатывание ститей
	$style = '';
	if( count( $jsam -> styleUrl ) ){
		$style = '@import url(' . implode( ");\n@import url(", $jsam -> styleUrl ) . ");\n";
	}
	if( count( $jsam -> style ) ){
		$jsam -> output( "<!-- JSAM Style Start -->\n<style type='text/css'>\n" . $style . implode( "\n", $jsam -> style ) . "\n</style>\n<!-- JSAM Style End -->\n" );
	}
	
	// Печать конечного кода
	$modx -> invokeEvent( 'OnJsamPrintCode', array( $jsam -> code ) );
	$jsam -> printCode();
	
	// Аналог плагина Inherit Parent Template
	if( $jsam -> type == 'document' && $jsam -> action == 4 && $jsam -> parentInfo[ 'id' ] != 0 ){
		$jsam -> event( 'OnDocFormPrerender', 'global $content; $content[ "template" ] = ' .  $jsam -> parentInfo[ 'template' ] . ';' );
	}
	
}
?>